package ckw.database.model;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import core.communication.configurationNode.CNode;
import core.communication.message.NodeStateEnum;
import core.database.AbstractEntity;
import core.database.NodeType;
import core.utils.LocatorUriChecker;

@Entity
@Table(name = "node")
public class Node extends AbstractEntity {

	@Column(name = "name", nullable = false)
	private String name;

	@Column(name = "ip", nullable = false, length = 40)
	private String ip;

	@Column(name = "port", nullable = false, length = 5)
	private String port;

	@Column(name = "address", nullable = false)
	private String address;

	@Column(name = "level", nullable = false)
	private int level;

	@Column(name = "nodeType", nullable = false)
	@Enumerated(EnumType.STRING)
	private NodeType nodeType;

	@ManyToOne
	@JoinColumn(name = "parent")
	private Node parent = null;

	@OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
	private List<Node> children = new LinkedList<Node>();

	@Embedded
	private NodeState state;

	@Column(name = "upToDate")
	private Boolean upToDate = true;

	@OneToMany(mappedBy = "node", fetch = FetchType.EAGER)
	private List<ElectionNode> elections = new ArrayList<ElectionNode>();

	@OneToMany(mappedBy = "node", fetch = FetchType.LAZY)
	private List<Voter> voters = new ArrayList<Voter>();

	public Node() {
		this.state = new NodeState(NodeStateEnum.ON);
	}

	public Node(String name, String ip, String port, String address, NodeType nodeType, Node parent) {
		super();
		this.name = name;
		this.ip = ip;
		this.port = port;
		this.address = address;
		this.nodeType = nodeType;
		this.parent = parent;
		if (parent != null) {
			this.level = parent.getLevel() + 1;
			parent.getChildren().add(this);
		} else {
			this.level = -1;
		}
		this.state = new NodeState(NodeStateEnum.ON);

	}

	public CNode toCNode() {
		return new CNode(name, address, ip, port, nodeType);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public String getPort() {
		return port;
	}

	public void setPort(String port) {
		this.port = port;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public NodeType getNodeType() {
		return nodeType;
	}

	public void setNodeType(NodeType nodeType) {
		this.nodeType = nodeType;
	}

	public Node getParent() {
		return parent;
	}

	public void setParent(Node parent) {
		this.parent = parent;
		this.level = parent.getLevel() + 1;
		this.parent.getChildren().add(this);
	}

	public List<Node> getChildren() {
		return children;
	}

	public void setChildren(List<Node> children) {
		this.children = children;
	}

	public NodeState getState() {
		return state;
	}

	public void setState(NodeState state) {
		this.state = state;
	}

	public Boolean getUpToDate() {
		return upToDate;
	}

	public void setUpToDate(Boolean upToDate) {
		this.upToDate = upToDate;
	}

	public List<ElectionNode> getElections() {
		return elections;
	}

	public void setElections(List<ElectionNode> elections) {
		this.elections = elections;
	}

	public List<Voter> getVoters() {
		return voters;
	}

	public void setVoters(List<Voter> voters) {
		this.voters = voters;
	}

	public String getLocatorURI() {
		return LocatorUriChecker.getLocatorURI(getIp(), getPort());
	}

	public void removeFromParent() {
		this.parent.getChildren().remove(this);
		this.parent = null;
	}

	public void changeState(NodeStateEnum stateEnum) {
		this.state.setStateEnum(stateEnum);
		this.state.prePersist();
	}
}